package org.biubiu0002.baiblog.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.commons.lang3.StringUtils;
import org.biubiu0002.baiblog.common.constants.CacheConstants;
import org.biubiu0002.baiblog.common.constants.Constants;
import org.biubiu0002.baiblog.common.constants.UserConstants;
import org.biubiu0002.baiblog.common.exception.BaiBlogException;
import org.biubiu0002.baiblog.common.exception.BizCodeEnumException;
import org.biubiu0002.baiblog.core.dao.SysUserDao;
import org.biubiu0002.baiblog.core.dao.SysUserRoleDao;
import org.biubiu0002.baiblog.core.model.dto.TokenUserDto;
import org.biubiu0002.baiblog.core.model.entity.SysUserEntity;
import org.biubiu0002.baiblog.core.model.entity.SysUserRoleEntity;
import org.biubiu0002.baiblog.core.model.vo.req.RegisterReqVo;
import org.biubiu0002.baiblog.core.model.vo.res.LoginResVo;
import org.biubiu0002.baiblog.core.model.vo.res.RegisterResVo;
import org.biubiu0002.baiblog.core.service.AuthService;
import org.biubiu0002.baiblog.core.service.SysUserService;
import org.biubiu0002.baiblog.core.utils.IdGenerator;
import org.biubiu0002.baiblog.core.utils.UidUtils;
import org.biubiu0002.baiblog.core.validator.AuthValidator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Author: biubiu0002
 * @Date: 2022-05-05 22:57
 **/
@Service
public class AuthServiceImpl implements AuthService {

    @Resource
    private SysUserService sysUserService;

    @Resource
    private SysUserDao sysUserDao;

    @Resource
    private SysUserRoleDao sysUserRoleDao;

    @Autowired
    private StringRedisTemplate redisTemplate;

    private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    private static final long TOKEN_TTL = 7 * 24 * 60 * 60L;

    @Override
    public LoginResVo login(SysUserEntity sysUserEntity) {
        SysUserEntity dbUser = sysUserDao.selectOne(new QueryWrapper<SysUserEntity>()
                .eq("username", sysUserEntity.getUsername()).eq("is_deleted",0) );
        if(dbUser==null){
            throw new BaiBlogException(BizCodeEnumException.LOGIN_FAIL_EXCEPTION);
        }
        boolean matches = passwordEncoder.matches(sysUserEntity.getPassword(), dbUser.getPassword());
        if(matches){
            List<String> roles =  sysUserDao.selectRolesByUserUid(dbUser.getResourceId());

            TokenUserDto tokenUserDto = new TokenUserDto(dbUser.getResourceId(), dbUser.getUsername(), roles);
            tokenUserDto.setNickname(dbUser.getNickname());
            tokenUserDto.setAvatar(dbUser.getAvatar());
            //生成token保存到redis
            String token = generateToken(tokenUserDto);

            LoginResVo loginResVo = new LoginResVo();
            loginResVo.setUserUid(dbUser.getResourceId());
            loginResVo.setUsername(dbUser.getUsername());
            loginResVo.setToken(token);
            loginResVo.setRoles(roles);
            loginResVo.setNickname(tokenUserDto.getNickname());
            loginResVo.setAvatar(tokenUserDto.getAvatar());
            return loginResVo;

        }else{
            throw new BaiBlogException(BizCodeEnumException.LOGIN_FAIL_EXCEPTION);
        }
    }

    public LoginResVo doLogin(String uid){
        SysUserEntity userEntity = sysUserService.getUserByUserUid(uid);
        List<String> roles = sysUserService.getRoleNamesByUserUid(uid);
        TokenUserDto tokenUserDto = new TokenUserDto();
        tokenUserDto.setUserUid(uid);
        tokenUserDto.setUsername(userEntity.getUsername());
        tokenUserDto.setRoles(roles);
        tokenUserDto.setNickname(userEntity.getNickname());
        tokenUserDto.setAvatar(userEntity.getAvatar());
        this.generateToken(tokenUserDto);
        LoginResVo loginResVo = new LoginResVo();
        BeanUtils.copyProperties(tokenUserDto,loginResVo);
        return loginResVo;
    }




    @Override
    public RegisterResVo register(RegisterReqVo registerReqVo) {
        AuthValidator.registerValidate(registerReqVo);
        SysUserEntity toRegisterUser = new SysUserEntity();
        toRegisterUser.setUsername(registerReqVo.getUsername());
        validateUser(toRegisterUser,registerReqVo.getUserType().getCode());
        if(registerReqVo.getUserType()== UserConstants.UserTypeEnum.ADMIN){
            validateRegisterSecret(registerReqVo.getSecret());
        }

        SysUserEntity dbUser = sysUserDao.selectOne(new QueryWrapper<SysUserEntity>().eq("username", registerReqVo.getUsername()));
        if(dbUser==null){
            //生成雪花id
            toRegisterUser.setId(IdGenerator.generateId());
            //生成uuid
            toRegisterUser.setResourceId(UidUtils.generateUid());
            //头像
            toRegisterUser.setAvatar(StringUtils.isNotBlank(registerReqVo.getAvatar())?registerReqVo.getAvatar():"https://picx.zhimg.com/80/v2-6afa72220d29f045c15217aa6b275808_720w");
            //默认昵称
            toRegisterUser.setNickname(StringUtils.isNotBlank(registerReqVo.getNickname())?
                    registerReqVo.getNickname():
                    "用户"+ UUID.randomUUID().toString().substring(0,8));
            //邮箱为""
            toRegisterUser.setEmail(StringUtils.isNotBlank(registerReqVo.getEmail())?registerReqVo.getEmail():"");
            //默认头像
        }else{
            BeanUtils.copyProperties(dbUser,toRegisterUser);
        }
        //密码加密 首次注册设置密码 提升权限时重设密码
        toRegisterUser.setPassword(passwordEncoder.encode(registerReqVo.getPassword()));
        //插入到用户表
        sysUserDao.saveOrUpdate(toRegisterUser);
        //插入到用户角色表
        sysUserRoleDao.insert(new SysUserRoleEntity(
                IdGenerator.generateId(),
                toRegisterUser.getResourceId(), registerReqVo.getUserType().getCode()));

        RegisterResVo registerResVo = new RegisterResVo();
        registerResVo.setUserUid(toRegisterUser.getResourceId());
        registerResVo.setUsername(toRegisterUser.getUsername());
        return registerResVo;


    }

    @Override
    public TokenUserDto getTokenUser(String token) {
        String json = redisTemplate.opsForValue().get(CacheConstants.getTokenKey(token));
        if(StringUtils.isNotBlank(json)){
            //key续期
            redisTemplate.expire(CacheConstants.getTokenKey(token),TOKEN_TTL,TimeUnit.SECONDS);
            return JSON.parseObject(json,TokenUserDto.class);
        }
        return null;
    }

    @Override
    public void setTokenUser(TokenUserDto tokenUser) {
        if(tokenUser==null){
            return;
        }
        String token = tokenUser.getToken();
        String tokenUserJson = JSON.toJSONString(tokenUser);
        redisTemplate.opsForValue().set(CacheConstants.getTokenKey(token),tokenUserJson);
    }

    @Override
    public void logout(HttpServletRequest request) {
        String token = request.getHeader("token");
        if(StringUtils.isBlank(token)){
            return;
        }
        redisTemplate.delete(CacheConstants.getTokenKey(token));
    }

    @Override
    public SysUserEntity getByUsername(String username) {
        return sysUserDao.selectUserByUsernameAndType(username, UserConstants.UserTypeEnum.USER.getCode());
    }


    @Override
    public List<SysUserEntity> getByUsernameLike(String name) {
        List<SysUserEntity> list = sysUserDao.selectUserByUsernameCollisionLike(name);
        if(CollectionUtils.isEmpty(list)){
           return Collections.emptyList();
        }
        return list;
    }


    private void validateUser(SysUserEntity sysUserEntity,int type) {
        SysUserEntity dbUser = sysUserDao.selectUserByUsernameAndType(sysUserEntity.getUsername(),type);
        if(dbUser!=null){
            throw new BaiBlogException(BizCodeEnumException.USERNAME_EXISTED_EXCEPTION);
        }
        if(StringUtils.isNotBlank(sysUserEntity.getPhone())){
            dbUser = sysUserDao.selectUserByPhoneAndType(sysUserEntity.getPhone(),type);
            if(dbUser!=null){
                throw new BaiBlogException(BizCodeEnumException.PHONE_EXISTED_EXCEPTION);
            }
        }
        if(StringUtils.isNotBlank(sysUserEntity.getEmail())){
            dbUser = sysUserDao.selectUserByEmailAndType(sysUserEntity.getEmail(),type);
            if(dbUser!=null){
                throw new BaiBlogException(BizCodeEnumException.EMAIL_EXISTED_EXCEPTION);
            }
        }
    }

    private void validateRegisterSecret(String secret){
        if(!StringUtils.equals(secret, Constants.REGISTER_SECRET)){
            throw new BaiBlogException(BizCodeEnumException.REGISTER_ADMIN_SECRET_WRONG_EXCEPTION);
        }

    }


    public String generateToken(TokenUserDto tokenUserDto){
        String token = UidUtils.generateUid();
        tokenUserDto.setToken(token);
        redisTemplate.opsForValue().set(CacheConstants.getTokenKey(token), JSON.toJSONString(tokenUserDto),TOKEN_TTL, TimeUnit.SECONDS);
        return token;
    }






}
